package com.boot.security.service.authorization;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.AuthorizationServiceException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;
import org.springframework.security.access.vote.AbstractAccessDecisionManager;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * @author 霜寒 <1621856595@qq.com>
 * @description 权限验证处理
 * @date 2020/2/18 18:11
 **/
@Slf4j
@Component
public class TokenAuthorizationManager extends AbstractAccessDecisionManager {

    public TokenAuthorizationManager() {
        super(Arrays.asList(
                new TokenAccessDecisionVoter(),
                new WebExpressionVoter(),
                new PreInvocationAuthorizationAdviceVoter(new ExpressionBasedPreInvocationAdvice()),
                new RoleVoter()
        ));
    }

    public TokenAuthorizationManager(List<AccessDecisionVoter<?>> decisionVoters) {
        super(decisionVoters);
    }

    @SuppressWarnings("unchecked")
    @Override
    public void decide(Authentication auth, Object object, Collection<ConfigAttribute> attributes) throws AccessDeniedException, InsufficientAuthenticationException {
        if (auth == null) {
            throw new AuthorizationServiceException("用户未登录");
        } else {
            List<AccessDecisionVoter<?>> decisionVoters = getDecisionVoters();
            for (AccessDecisionVoter voter : decisionVoters) {
                if (voter.supports(object.getClass()) &&
                        AccessDecisionVoter.ACCESS_DENIED == voter.vote(auth, object, attributes)) {
                    log.info("用户 {} 鉴权失败", auth.getName());
                    throw new AuthorizationServiceException(voter.getClass().getName() + " 投否决票");
                }
            }
            log.info("用户 {} 鉴权成功", auth.getName());
        }
    }

    @Override
    public boolean supports(Class<?> clazz) {
        List<AccessDecisionVoter<?>> decisionVoters = getDecisionVoters();
        for (AccessDecisionVoter<?> voter : decisionVoters) {
            if (voter.supports(clazz)) {
                return true;
            }
        }
        return false;
    }
}
